import pygame
import os

from copy import copy

from settings import settings
import events
from maps import Map

from collision import CollisionLayer, Rect

class Game(object):
	tracks = [os.path.join(settings.paths.music, node) 
		for node in os.listdir(settings.paths.music)
		if os.path.isfile(os.path.join(settings.paths.music, node))]
	
	def __init__(self, campaign, level):
		self.objects = set()
		self.entries = {}
		self.selected = set()
		self.criticals = set()
		self.damages = set()
		self.sockets = set()
		
		self.level = campaign.level(level, self)
		self.map = Map(self, self.level.map)
		self.rect = self.map.rect
		
		self.collision_layers = {
			"units": CollisionLayer(self.map.size),
			"bullets": CollisionLayer(self.map.size),
			"sockets": CollisionLayer(self.map.size)}
		
		for socket in self.sockets:
			self.collision_layers["sockets"].add(socket, socket.rect)
		
		# Catalogue all nodes
		self.nodes = set()
		for node in self.entries.itervalues():
			def add_root(node):
				"""Search the sub-graph for all nodes not currently in the
				catalogue and add them and the sub-graphs extending from them"""
				if node not in self.nodes:
					self.nodes.add(node)			
					for child in node.children:
						add_root(child)
			
			for node in self.entries.itervalues():
				add_root(node)
		
		self.paused = False
		
		self.waves = self.level.stage.waves
		
		self.frame = 0
		self.viewport = pygame.Rect(0, 0, 640, 480)
		
		if self.level.tracks is not None:
			self.tracks = self.level.tracks
		
		for object in self.objects:
			self.map.render_layers[object.render_layer].add(object)
		
		map_width, map_height = self.map.size
		tile_width, tile_height = self.map.tile_size
		size = (map_width * tile_width, map_height * tile_height)
		
		self.crosshairs = pygame.Surface(size).convert_alpha()
	
	def update(self):
		if not self.paused:
			self.frame += 1
			self.level.update(self.frame)
			self.map.update(self.frame)
			
			self.collision_layers["units"].clear()
			
			for wave in self.level.stage.waves:
				for spawn in wave.spawn(self, self.frame):
					self.add_object(spawn)
					
			for object in copy(self.objects):
				object.update()
		
			for object in self.objects:
				if object.collisions: 
					object.collisions()
			
			for damage in self.damages:
				damage.apply(self.collision_layers["units"])
			else:
				self.damages.clear()
		
		if self.criticals and all(object.health <= 0 for object in self.criticals):
			event = pygame.event.Event(events.LEVELCHANGE,
				campaign=self.level.campaign, level=self.level.levelname)
			pygame.event.post(event)
	
	def draw(self, surface, debug=False):		
		self.map.draw(surface, self.viewport)
		
		if debug:
			self.level.graph.draw(surface, pygame.Rect(100, 0, 540, 20), 
				self.frame)
		
			for node in self.nodes:
				node.draw_links(surface)
		
		self.crosshairs.fill((0, 0, 0, 0))
		self.crosshairs.set_alpha(128)
		for turret in self.selected:
			turret.crosshair(self.crosshairs)
		surface.blit(self.crosshairs, (0, 0))
	
	def event(self, event):
		if event.type == events.PAUSE:
			self.paused = not self.paused
		elif event.type == pygame.MOUSEBUTTONDOWN:	
			x, y = event.pos
			rect = Rect(x, y, 1, 1)
			
			sockets = self.collision_layers["sockets"].objects_within_rect(rect)
			turrets = set(socket.turret for socket in sockets 
				if socket.rect.colliderect(rect))
			
			if event.button == 1:
				self.selected |= turrets
				
				for turret in turrets:
					turret.selected()
				
			elif event.button == 3:
				self.selected -= turrets
				
				for turret in turrets:
					turret.deselected()
		else:
			for object in copy(self.objects):
				object.event(event)

	def add_object(self, object):
		self.objects.add(object)
		self.map.render_layers[object.render_layer].add(object)

	def remove_object(self, object):
		self.objects.remove(object)
		self.map.render_layers[object.render_layer].remove(object)

class AutoGame(Game):
	paused = property(lambda self: False, lambda self, x: None)

class MenuGame(AutoGame):
	tracks = [os.path.join(settings.menu.music, node) 
		for node in os.listdir(settings.menu.music)
		if os.path.isfile(os.path.join(settings.menu.music, node))]

